import java.awt.Point;
import java.awt.event.KeyEvent;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Iterator;

import javax.swing.JFileChooser;

public class FKeyEventProcessor extends DrawObjectEventProcessor
{
	// NOTE: ACTUALLY NO DRAW OBJECT, BUT KEYBOARD EVENTS PROCESSED IN THE SAME WAY!

	private final String SimulationTitleBarInfo1 = "Manual Activation (Space)";
	private final String SimulationTitleBarInfo2 = "Simulation Running";
	private final String SaveEmptyFileErrorMessage = "Error saving file: you cannot override the empty file - just load it (F1) to begin a new project from scratch and then save it (F3) with a different name.";

	private IOProgram IOProgram;
	private LoadSaveManager LoadSaveManager;
	private ObjectStorage ObjectStorage;
	private Settings Settings;

	private boolean ReducedDrawModeEnabled = false;

	private ArrayList<DrawObject> CopyAndPasteStartDrawObjects = new ArrayList<DrawObject>();

	public FKeyEventProcessor(IOProgram IOProgram, LoadSaveManager LoadSaveManager, ObjectStorage ObjectStorage, Settings Settings)
	{
		this.IOProgram = IOProgram;
		this.LoadSaveManager = LoadSaveManager;
		this.ObjectStorage = ObjectStorage;
		this.Settings = Settings;
	}

	private ArrayList<DrawObject> GetCopyAndPasteEndDrawObjects(ArrayList<DrawObject> DrawObjects)
	{
		ArrayList<DrawObject> CopyAndPasteEndDrawObjects = new ArrayList<DrawObject>();

		for (Iterator<DrawObject> i = DrawObjects.iterator(); i.hasNext();)
		{
			DrawObject o = i.next();
			if (o instanceof Node ||
				o instanceof SampleAndHoldInputNode ||
				o instanceof TextField ||
				o instanceof VisualFieldPixel)
			{
				CopyAndPasteEndDrawObjects.add(o);
			}
		}

		return CopyAndPasteEndDrawObjects;
	}

	private ArrayList<DrawObject> GetCopyAndPasteStartDrawObjects(ArrayList<DrawObject> DrawObjects)
	{
		ArrayList<DrawObject> CopyAndPasteStartDrawObjects = new ArrayList<DrawObject>();

		for (Iterator<DrawObject> i = DrawObjects.iterator(); i.hasNext();)
		{
			DrawObject o = i.next();
			if (o instanceof NeuronOutputNode ||
				o instanceof Node ||
				o instanceof SampleAndHoldOutputNode ||
				o instanceof TextField ||
				o instanceof VisualFieldPixel)
			{
				CopyAndPasteStartDrawObjects.add(o);
			}
		}

		return CopyAndPasteStartDrawObjects;
	}

	private LoadSaveDataEx LoadFile(String LoadPath, LoadSaveManager LoadSaveManager, boolean LoadNewOrInsert)
	{
		LoadSaveDataEx LoadSaveDataEx;

		if (LoadNewOrInsert)
		{
			LoadSaveDataEx = ObjectStorage.LoadAllFromFile(LoadPath);
		}
		else
		{
			int DrawObjectCountOld = ObjectStorage.GetDrawObjectCount();

			LoadSaveDataEx = ObjectStorage.LoadAdditionalFromFile(LoadPath);

			ObjectStorage.UnSetPartOfMultiSelectionForAllObjects();

			int DrawObjectCountNew = ObjectStorage.GetDrawObjectCount();

			for (int i = DrawObjectCountOld; i < DrawObjectCountNew; i++)
				ObjectStorage.SetPartOfMultiSelection(i);
		}

		return LoadSaveDataEx;
	}

	@SuppressWarnings("static-access")
	private String PromptLoadFile(MainWindow.GUIElements GUIElements, LoadSaveManager LoadSaveManager, boolean LoadNewOrInsert)
	{
		String DialogTitle = "";

		if (LoadNewOrInsert)
		{
			DialogTitle = "Load file";
		}
		else
		{
			DialogTitle = "Insert file content";
		}

		JFileChooser Dialog = new JFileChooser(); // https://www.codejava.net/java-se/swing/show-save-file-dialog-using-jfilechooser

		Dialog.setDialogTitle(DialogTitle);
		Dialog.setCurrentDirectory(new File(Settings.GetWorkingDirectory()));

		int DialogResult = Dialog.showSaveDialog(GUIElements.GetMainWindow());

		if (DialogResult == JFileChooser.APPROVE_OPTION)
		{
			return Dialog.getSelectedFile().getAbsolutePath();
		}
		else
		{
			return "";
		}
	}

	@SuppressWarnings("static-access")
	private String PromptSaveFile(MainWindow.GUIElements GUIElements, LoadSaveManager LoadSaveManager, boolean SaveNewOrOverride)
	{
		if (SaveNewOrOverride)
		{
			String DialogTitle = "Save as .ncide file";

			JFileChooser Dialog = new JFileChooser();

			Dialog.setDialogTitle(DialogTitle);
			Dialog.setCurrentDirectory(new File(Settings.GetWorkingDirectory())); // TEST, make this dynamic

			int DialogResult = Dialog.showSaveDialog(GUIElements.GetMainWindow()); // https://www.codejava.net/java-se/swing/show-save-file-dialog-using-jfilechooser

			if (DialogResult == JFileChooser.APPROVE_OPTION)
			{
				String SavePath = Dialog.getSelectedFile().getAbsolutePath();
				String SavePathSuffix = ".ncide";

				// add file name suffix if not there
				if (SavePath.length() < SavePathSuffix.length() || NOT(SavePath.substring(SavePath.length() - SavePathSuffix.length()).toLowerCase().equals(SavePathSuffix.toLowerCase()))) // not already ".ncide" at end of file name?
				{
					SavePath += SavePathSuffix;
				}

				String WorkingDirectoryNew = "";

				int LastSlashPos = SavePath.lastIndexOf("/");
				if (LastSlashPos < 0)
					LastSlashPos = SavePath.lastIndexOf("\\");
				if (LastSlashPos < 0)
					LastSlashPos = 0;

				WorkingDirectoryNew = SavePath.substring(0, LastSlashPos);

				// working directory is just the last directory
				// where the user manually saved a file

				Settings.SetWorkingDirectory(WorkingDirectoryNew);
				Settings.SaveSettingsFile();

				VerifyEmptyFileExists(Settings.GetWorkingDirectory());

				return SavePath;
			}
			else
			{
				return "";
			}
		}
		else
		{
			return ""; // this function is not suitable for this file saving mode
		}
	}

	private LoadSaveDataEx SaveFile(MainWindow MainWindow, String SavePath, LoadSaveDataEx LoadSaveDataEx, LoadSaveManager LoadSaveManager, boolean SaveOrOverride)
	{
		// create auto-backup
		try
		{
			String BackUpPath = "";

			Path CopySourcePath = Paths.get(SavePath);
			Path CopyTargetPath = null;

			for (int m = 1; m < 1000; m++)
			{
				int SavePathLastSlashPos = SavePath.lastIndexOf("/");
				if (SavePathLastSlashPos < 0)
					SavePathLastSlashPos = SavePath.lastIndexOf("\\");
				if (SavePathLastSlashPos < 0)
					SavePathLastSlashPos = -1;

				String BackUpDir = SavePath.substring(0, SavePathLastSlashPos + 1) + "BackUp/";

				if (!((new File(BackUpDir)).exists()))
					(new File(BackUpDir)).mkdirs();

				BackUpPath = BackUpDir + "{backup" + String.format("%04d", m) + "." + SavePath.substring(SavePathLastSlashPos + 1) + "}";

				System.out.println(BackUpPath);

				CopyTargetPath = Paths.get(BackUpPath);

				if (NOT(Files.exists(CopyTargetPath)))
					break;
				else
					CopyTargetPath = null;
			}
			if (CopySourcePath != null && CopyTargetPath != null)
			{
				Files.copy(CopySourcePath, CopyTargetPath, StandardCopyOption.REPLACE_EXISTING);
			}
		}
		catch (Exception e)
		{
			System.out.println("error creating backup of file to override: " + e.getMessage()); // just go on (for now)
		}

		// do actual saving
		LoadSaveDataEx LoadSaveDataExReturned = ObjectStorage.SaveAllToFile(SavePath, LoadSaveDataEx);

		return LoadSaveDataExReturned;
	}

	public void ScrollAfterZoomFactorChange(MainWindow.GUIElements GUIElements, MouseEventProcessor MouseEventProcessor, Point MousePosUnZoomedBeforeZoomFactorChange)
	{
		Point MousePosUnZoomedAfter = DrawObject.AlignOnGrid(DrawObject.UnZoomPoint(MouseEventProcessor.GetMousePosPoint(GUIElements.GetMainWindow())));

		Point MoveAmount = new Point(MousePosUnZoomedAfter.x - MousePosUnZoomedBeforeZoomFactorChange.x, MousePosUnZoomedAfter.y - MousePosUnZoomedBeforeZoomFactorChange.y); // new pos mouse is pointing to (after zooming) shall lie at old one (before zooming)

		ObjectStorage.MoveAll(MoveAmount);

		ShowZoomFactorChangeMessage(GUIElements);
	}

	private void ShowZoomFactorChangeMessage(MainWindow.GUIElements GUIElements)
	{
		GUIElements.GetMainWindow().GetTitleBarInfo().RemoveInfoTextContaining("Zoom ");
		GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText(ZoomManager.GetZoomInfoText(ZoomManager.GetZoomFactor()), true, false, false, false);
	}

	private void StartSimulation(MainWindow.GUIElements GUIElements, boolean ManualActivationOnly)
	{
		GUIElements.GetMainWindow().EnableHandsomeDrawOrder(); // draw VisualFields and VisualFieldsPixels at last to not be hidden by Lines

		GUIElements.GetIOPanel().GetIOTextArea().setEditable(false);

		IOProgram.StartExecution(GUIElements, ObjectStorage, ManualActivationOnly);

		if (ManualActivationOnly)
			GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText(SimulationTitleBarInfo1, false, false, true, false);
		else
			GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText(SimulationTitleBarInfo2, false, false, true, false);
	}

	private void StopSimulation(MainWindow.GUIElements GUIElements)
	{
		GUIElements.GetMainWindow().DisableHandsomeDrawOrder(); // draw again in exactly that order the DrawObjects were created/added

		IOProgram.StopExecution();

		// NOTE: there was a problem with vanished keyboard cursor,
		// as a counter measure, try all of the following:
		
		GUIElements.GetIOPanel().GetIOTextArea().setEnabled(true);
		GUIElements.GetIOPanel().GetIOTextArea().setVisible(true);
		GUIElements.GetIOPanel().GetIOTextArea().setEditable(true);
		GUIElements.GetIOPanel().GetIOTextArea().getCaret().setVisible(true); // https://groups.google.com/g/comp.lang.java.programmer/c/VcjWydFYR8E

		GUIElements.GetMainWindow().GetTitleBarInfo().RemoveInfoText(SimulationTitleBarInfo1);
		GUIElements.GetMainWindow().GetTitleBarInfo().RemoveInfoText(SimulationTitleBarInfo2);
	}

	@SuppressWarnings("static-access")
	@Override
	public void TryProcessUserKeyboardAction(MainWindow.GUIElements GUIElements, KeyEventProcessor KeyEventProcessor, MouseEventProcessor MouseEventProcessor, ObjectStorage ObjectStorage, IOProgram IOProgram)
	{
		if (!(KeyboardEventsToBeProcessed(GUIElements)))
			return;

		if (KeyEventProcessor.AnyKeyPressed())
		{
			// ****************************************************************
			// b (restore BackUp (actually no F-Key, but fits in here))
			if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_B) &&
				KeyEventProcessor.IsControlPressed())
			{
				GUIElements.GetMainWindow().EnableBackUpRestore(
					Settings.GetWorkingDirectory(),
					LoadSaveManager.GetOverrideFileName()
				);

				GUIElements.GetMainWindow().DoRedraw();
			}
			// ****************************************************************
			// F1 (load new file)
			if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_F1))
			{
				String LoadPath = PromptLoadFile(GUIElements, LoadSaveManager, true);

				if (LoadPath.length() > 0)
				{
					StopSimulation(GUIElements); // doesn't continue right if loading a new file, don't know exactly why, but continuing simulation is 1) confusing for the user and 2) doesn't really make sense

					GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("Loading file", false, true, true, true);

					LoadSaveDataEx LoadSaveDataEx = LoadFile(LoadPath, LoadSaveManager, true);

					if (LoadSaveDataEx.GetSuccessOrError())
					{
						LoadSaveManager.ReactOnFileLoaded(LoadPath, false);

						GUIElements.GetIOPanel().SetIOProgramString(LoadSaveDataEx.GetIOProgramString());
						IOProgram.SetIOProgramString(LoadSaveDataEx.GetIOProgramString());

						GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("LOADED", true, false, false, false);

						ZoomManager.SetZoomFactor(LoadSaveDataEx.GetZoomFactor());
						ShowZoomFactorChangeMessage(GUIElements);
					}
					else
					{
						LoadSaveManager.ReactOnFileLoadError();

						Tools.ShowMessageDialog(GUIElements.GetMainWindow(), "Error loading file: " + LoadSaveDataEx.GetErrorMessage());
					}

					GUIElements.GetMainWindow().GetTitleBarInfo().RemoveInfoText("Loading file");
				}

				GUIElements.GetMainWindow().DoRedraw(); // do in any case (to make sure GUI is up to date)
			}
			// ****************************************************************
			// F2 (load file content and insert)
			if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_F2))
			{
				String LoadPath = PromptLoadFile(GUIElements, LoadSaveManager, false);

				if (LoadPath.length() > 0)
				{
					GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("Loading file", false, true, true, true);

					LoadSaveDataEx LoadSaveDataEx = LoadFile(LoadPath, LoadSaveManager, false);

					if (LoadSaveDataEx.GetSuccessOrError())
					{
						LoadSaveManager.ReactOnFileLoaded(LoadPath, true);

						GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("INSERTED", true, false, false, false);
					}
					else
					{
						Tools.ShowMessageDialog(GUIElements.GetMainWindow(), "Error loading file: " + LoadSaveDataEx.GetErrorMessage());
					}

					GUIElements.GetMainWindow().GetTitleBarInfo().RemoveInfoText("Loading file");
				}

				GUIElements.GetMainWindow().DoRedraw(); // do in any case (to make sure GUI is up to date)
			}
			// ****************************************************************
			// F3 (save to file)
			if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_F3))
			{
				String SavePath = PromptSaveFile(GUIElements, LoadSaveManager, true);

				if (SavePath.length() > 0)
				{
					if (SavePath.toLowerCase().endsWith("empty.ncide"))
					{
						Tools.ShowMessageDialog(GUIElements.GetMainWindow(), SaveEmptyFileErrorMessage);
					}
					else
					{
						GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("Saving file", false, true, true, true);

						LoadSaveDataEx LoadSaveDataEx = SaveFile(GUIElements.GetMainWindow(), SavePath, new LoadSaveDataEx(true, "", GUIElements.GetIOPanel().GetIOProgramString(), ZoomManager.GetZoomFactor()), LoadSaveManager, true);

						if (LoadSaveDataEx.GetSuccessOrError())
						{
							LoadSaveManager.ReactOnFileSaved(SavePath, false);

							GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("SAVED", true, false, false, false);
						}
						else
						{
							Tools.ShowMessageDialog(GUIElements.GetMainWindow(), "Error saving file: " + LoadSaveDataEx.GetErrorMessage());
						}

						GUIElements.GetMainWindow().GetTitleBarInfo().RemoveInfoText("Saving file");
					}
				}

				GUIElements.GetMainWindow().DoRedraw(); // do in any case (to make sure GUI is up to date)
			}
			// ****************************************************************
			// F4 (override last loaded or saved file)
			if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_F4))
			{
				String SavePath = LoadSaveManager.GetOverridePath();

				if (SavePath != null && SavePath.length() > 0)
				{
					if (SavePath.toLowerCase().endsWith("empty.ncide"))
					{
						Tools.ShowMessageDialog(GUIElements.GetMainWindow(), SaveEmptyFileErrorMessage);
					}
					else
					{
						GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("Saving file", false, true, true, true);

						LoadSaveDataEx LoadSaveDataEx = SaveFile(GUIElements.GetMainWindow(), SavePath, new LoadSaveDataEx(true, "", GUIElements.GetIOPanel().GetIOProgramString(), ZoomManager.GetZoomFactor()), LoadSaveManager, false);

						if (LoadSaveDataEx.GetSuccessOrError())
						{
							LoadSaveManager.ReactOnFileSaved(SavePath, true);

							GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("SAVED", true, false, false, false);
						}
						else
						{
							Tools.ShowMessageDialog(GUIElements.GetMainWindow(), "Error overriding file: " + LoadSaveDataEx.GetErrorMessage());
						}

						GUIElements.GetMainWindow().GetTitleBarInfo().RemoveInfoText("Saving file");
					}
				}
				else
				{
					Tools.ShowMessageDialog(GUIElements.GetMainWindow(), "Error overriding file: No (valid) file to override loaded or saved before");
				}

				GUIElements.GetMainWindow().DoRedraw(); // do in any case (to make sure GUI is up to date)
			}
			// ****************************************************************
			// F7 (copy selection or nodes to clipboard (actually a temp file resp. ArrayList))
			if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_F7) &&
				NOT(KeyEventProcessor.IsControlPressed()) &&
				NOT(KeyEventProcessor.IsShiftPressed()))
			{
				GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("Copying", false, true, true, true);

				LoadSaveDataEx LoadSaveDataEx = ObjectStorage.SaveMultiSelectionToFile("./NCIDE_clipboard.dat", new LoadSaveDataEx(true, "", GUIElements.GetIOPanel().GetIOProgramString(), ZoomManager.GetZoomFactor()));

				GUIElements.GetMainWindow().GetTitleBarInfo().RemoveInfoText("Copying");

				if (LoadSaveDataEx.GetSuccessOrError())
				{
					GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("COPIED", true, false, false, false);
				}
				else
				{
					Tools.ShowMessageDialog(GUIElements.GetMainWindow(), "Error copying: " + LoadSaveDataEx.GetErrorMessage());
				}

				GUIElements.GetMainWindow().DoRedraw();
			}
			if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_F7) && (
				KeyEventProcessor.IsControlPressed() ||
				KeyEventProcessor.IsShiftPressed()))
			{
				GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("Copying", false, true, true, true);

				ArrayList<DrawObject> MultiSelectionObjects = ObjectStorage.GetMultiSelectionObjects();

				CopyAndPasteStartDrawObjects = GetCopyAndPasteStartDrawObjects(MultiSelectionObjects);

				ObjectStorage.UnSetPartOfMultiSelectionForAllObjects();

				GUIElements.GetMainWindow().GetTitleBarInfo().RemoveInfoText("Copying");

				GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("COPIED " + CopyAndPasteStartDrawObjects.size() + " CONNECTION OBJECTS", true, false, false, false);

				GUIElements.GetMainWindow().DoRedraw();
			}
			// ****************************************************************
			// F8 (paste content from clipboard resp. ArrayList)
			if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_F8) &&
				NOT(KeyEventProcessor.IsControlPressed()) &&
				NOT(KeyEventProcessor.IsShiftPressed()))
			{
				GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("Pasting", false, true, true, true);

				LoadSaveDataEx LoadSaveDataEx = LoadFile("./NCIDE_clipboard.dat", LoadSaveManager, false);

				GUIElements.GetMainWindow().GetTitleBarInfo().RemoveInfoText("Pasting");

				if (LoadSaveDataEx.GetSuccessOrError())
				{
					GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("PASTED", true, false, false, false);
				}
				else
				{
					Tools.ShowMessageDialog(GUIElements.GetMainWindow(), "Error pasting: " + LoadSaveDataEx.GetErrorMessage());
				}

				GUIElements.GetMainWindow().DoRedraw();
			}
			if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_F8) && (
				KeyEventProcessor.IsControlPressed() ||
				KeyEventProcessor.IsShiftPressed()))
			{
				GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("Pasting", false, true, true, true);

				ArrayList<DrawObject> MultiSelectionObjects = ObjectStorage.GetMultiSelectionObjects();

				ArrayList<DrawObject> CopyAndPasteEndDrawObjects = GetCopyAndPasteEndDrawObjects(MultiSelectionObjects);

				boolean NeuronDrawObjectsOnly = true;

				for (int m = 0; m < MultiSelectionObjects.size(); m++)
				{
					if (!(MultiSelectionObjects.get(m) instanceof Neuron) &&
						!(MultiSelectionObjects.get(m) instanceof NeuronInputField) &&
						!(MultiSelectionObjects.get(m) instanceof NeuronInputNode) &&
						!(MultiSelectionObjects.get(m) instanceof NeuronOutputNode))
					{
						NeuronDrawObjectsOnly = false;
						break;
					}
				}

				int LineCreatedCount = 0;

				if (MultiSelectionObjects.size() >= 1 &&
					NeuronDrawObjectsOnly)
				{
					for (int n = 0; n < MultiSelectionObjects.size(); n++)
					{
						if (MultiSelectionObjects.get(n) instanceof Neuron)
						{
							CopyAndPasteEndDrawObjects.clear();

							int InputCountRequired = CopyAndPasteStartDrawObjects.size();

							for (int m = 0; m < InputCountRequired; m++)
							{
								Neuron.NeuronInputDrawObjects InputDrawObjects = ((Neuron) MultiSelectionObjects.get(n)).AddInputFieldAndNode();

								ObjectStorage.AddObjectDirectly(InputDrawObjects.GetField());
								ObjectStorage.AddObjectDirectly(InputDrawObjects.GetNode());

								CopyAndPasteEndDrawObjects.add(InputDrawObjects.GetNode());
							}

							for (int i = 0; i < CopyAndPasteStartDrawObjects.size(); i++)
							{
								Line Line = new Line(CopyAndPasteStartDrawObjects.get(i), CopyAndPasteEndDrawObjects.get(i));

								ObjectStorage.AddObjectDirectly(Line);

								LineCreatedCount++;
							}
						}
					}
				}
				else if (CopyAndPasteStartDrawObjects.size() == CopyAndPasteEndDrawObjects.size())
				{
					for (int i = 0; i < CopyAndPasteStartDrawObjects.size(); i++)
					{
						Line Line = new Line(CopyAndPasteStartDrawObjects.get(i), CopyAndPasteEndDrawObjects.get(i));

						ObjectStorage.AddObjectDirectly(Line);

						LineCreatedCount++;
					}
				}

				ObjectStorage.UnSetPartOfMultiSelectionForAllObjects();

				GUIElements.GetMainWindow().GetTitleBarInfo().RemoveInfoText("Pasting");

				GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("PASTED " + LineCreatedCount + " LINES", true, false, false, false);

				GUIElements.GetMainWindow().DoRedraw();
			}
			// ****************************************************************
			// F9 (help)
			if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_F9))
			{
				GUIElements.GetMainWindow().ToggleHelpText();
				GUIElements.GetMainWindow().DoRedraw();
			}
			// ****************************************************************
			// F10 (show/hide IOPanel)
			if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_F10))
			{
				boolean IPanelVisible = GUIElements.GetIOPanel().isVisible();

				IPanelVisible = !IPanelVisible;

				GUIElements.GetIOPanel().setVisible(IPanelVisible);

				if (IPanelVisible)
				{
					GUIElements.GetIOPanel().requestFocus();

					GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("F11 or F12", true, true, false, false);
				}
				else
					GUIElements.GetMainWindow().requestFocus(); // important, or KeyListener _somehow_ "stays" in hidden TextArea

				GUIElements.GetMainWindow().DoRedraw(); // necessary (tested), or odd window salad
			}
			// ****************************************************************
			// F11 (start simulation, wait for manual activation (space bar) only)
			// F12 (start simulation, work up IOTextArea text and allow manual activation (space bar))
			if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_F11) ||
				KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_F12))
			{
				if (NOT(IOProgram.IsExecuted()))
				{
					if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_F11))
					{
						StartSimulation(GUIElements, true);
					}
					if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_F12))
					{
						StartSimulation(GUIElements, false);
					}
				}
				else
				{
					StopSimulation(GUIElements);
				}

				GUIElements.GetMainWindow().DoRedraw(); // do in any case (to make sure GUI is up to date)
			}
			if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_R) &&
				KeyEventProcessor.IsControlPressed())
			{
				ReducedDrawModeEnabled = !ReducedDrawModeEnabled;

				if (ReducedDrawModeEnabled)
					GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("Drawing less to speed up", true, false, false, false);
				else
					GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("Drawing all", true, false, false, false);

				DrawObject.SetReducedDrawOrderEnabled(ReducedDrawModeEnabled);

				GUIElements.GetMainWindow().DoRedraw();
			}

			// ****************************************************************
			// F5 (toggle two default zoom settings)
			// F6 (restore previous custom zoom)
			if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_F5) ||
				KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_F6))
			{
				Point MousePosUnZoomedBeforeZoomFactorChange = DrawObject.AlignOnGrid(DrawObject.UnZoomPoint(MouseEventProcessor.GetMousePosPoint(GUIElements.GetMainWindow())));

				if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_F5))
				{
					if (!(ZoomManager.IsZoomFactorFixedSmall(ZoomManager.GetZoomFactor())))
					{
						ZoomManager.SetZoomFactorFixedSmall();
					}
					else if (!(ZoomManager.IsZoomFactorFixedLarge(ZoomManager.GetZoomFactor()))) // ELSE if!
					{
						ZoomManager.SetZoomFactorFixedLarge();
					}
				}
				else if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_F6))
				{
					ZoomManager.SetZoomFactor(ZoomManager.GetZoomFactorCustomLast());
				}

				ScrollAfterZoomFactorChange(GUIElements, MouseEventProcessor, MousePosUnZoomedBeforeZoomFactorChange);
				
				GUIElements.GetMainWindow().DoRedraw();
			}
		}
	}

	@Override
	public void TryProcessUserMouseAction(MainWindow.GUIElements GUIElements, KeyEventProcessor KeyEventProcessor, MouseEventProcessor MouseEventProcessor, ObjectStorage ObjectStorage, IOProgram IOProgram)
	{
		// ********************************************************************
		// DO BEFORE CHECKING IF MOUSE EVENTS TO BE PROCESSED!
		// restore BackUp or end restoring mode
		// ->
		if (MouseEventProcessor.IsLeftButtonClicked() &&
			GUIElements.GetMainWindow().IsBackUpRestoreEnabled())
		{
			int BackUpFileIndex = (MouseEventProcessor.GetMousePosPoint(GUIElements.GetMainWindow()).y - 100) / 50; // must match font size used for drawing backup file list

			if (MouseEventProcessor.GetMousePosPoint(GUIElements.GetMainWindow()).y < 100 ||
				BackUpFileIndex < 0)
			{
				GUIElements.GetMainWindow().DisableBackUpRestore();
				GUIElements.GetMainWindow().DoRedraw();
			}
			else if (BackUpFileIndex >= 0 && BackUpFileIndex < GUIElements.GetMainWindow().GetBackUpRestoreFiles().size())
			{
				String LoadPath = GUIElements.GetMainWindow().GetBackUpRestoreFiles().get(BackUpFileIndex);

				if (LoadPath.length() > 0)
				{
					if (Tools.ShowYesNoDialog(GUIElements.GetMainWindow(), "Restore \"" + LoadPath + "\"?") == true)
					{
						StopSimulation(GUIElements); // doesn't continue right if loading a new file, don't know exactly why, but continuing simulation is 1) confusing for the user and 2) doesn't really make sense

						GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("Restoring Backup", false, true, true, true);

						LoadSaveDataEx LoadSaveDataEx = LoadFile(LoadPath, LoadSaveManager, true);

						if (LoadSaveDataEx.GetSuccessOrError())
						{
							// LoadSaveManager.ReactOnFileLoaded(LoadPath, false);

							GUIElements.GetIOPanel().SetIOProgramString(LoadSaveDataEx.GetIOProgramString());
							IOProgram.SetIOProgramString(LoadSaveDataEx.GetIOProgramString());

							GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("BACKUP RESTORED", true, false, false, false);

							ZoomManager.SetZoomFactor(LoadSaveDataEx.GetZoomFactor());
							ShowZoomFactorChangeMessage(GUIElements);
						}
						else
						{
							// LoadSaveManager.ReactOnFileLoadError();

							Tools.ShowMessageDialog(GUIElements.GetMainWindow(), "Error restoring Backup: " + LoadSaveDataEx.GetErrorMessage());
						}

						GUIElements.GetMainWindow().GetTitleBarInfo().RemoveInfoText("Restoring Backup");

						GUIElements.GetMainWindow().DisableBackUpRestore();
					}
				}

				GUIElements.GetMainWindow().DoRedraw(); // do in any case (to make sure GUI is up to date)
			}
		}
		// ********************************************************************

		if (!(MouseEventsToBeProcessed(GUIElements)))
			return;

		// ********************************************************************
		if (MouseEventProcessor.WasMouseWheelTurned())
		{
			Point MousePosUnZoomedBeforeZoomFactorChange = DrawObject.AlignOnGrid(DrawObject.UnZoomPoint(MouseEventProcessor.GetMousePosPoint(GUIElements.GetMainWindow())));

			if (MouseEventProcessor.WasMouseWheelTurnedUp())
			{
				double ZoomStep = (ZoomManager.GetZoomFactor() >= 0.10 ? 0.05 : 0.01);

				ZoomManager.SetZoomFactor(ZoomManager.GetZoomFactor() - ZoomStep);
			}
			if (MouseEventProcessor.WasMouseWheelTurnedDown())
			{
				double ZoomStep = (ZoomManager.GetZoomFactor() >= 0.05 ? 0.05 : 0.01);

				ZoomManager.SetZoomFactor(ZoomManager.GetZoomFactor() + ZoomStep);
			}

			ScrollAfterZoomFactorChange(GUIElements, MouseEventProcessor, MousePosUnZoomedBeforeZoomFactorChange);
			
			GUIElements.GetMainWindow().DoRedraw();
		}

		return;
	}

	private void VerifyEmptyFileExists(String WorkingDirectory)
	{
		String EmptyNCIDEFilePath = (WorkingDirectory + "/" + "empty.ncide").replaceAll("//", "/");

		if ((new File(EmptyNCIDEFilePath)).exists() == false)
		{
			ArrayList<DrawObject> EmptyDrawObjects = new ArrayList<DrawObject>();

			EmptyDrawObjects.clear(); // just to make sure there's nothing in it

			ObjectStorage.SaveEmptyFile(EmptyNCIDEFilePath);
		}
	}
}
